
#include "IrrCompileConfig.h"

#ifdef _IRR_WINDOWS_

#include "CIrrDeviceWin32.h"
#include "IEventReceiver.h"
#include "irrList.h"
#include "os.h"

#include "CTimer.h"
#include <stdio.h>
#include "irrString.h"
#include "COSOperator.h"
#include "dimension2d.h"
#include <winuser.h>
#include "irrlicht.h"

namespace irr
{
    namespace video
    {
        IVideoDriver* createOpenGLDriver(const core::dimension2d<s32>& screenSize, HWND window, 
            bool fullscreen, bool stencilBuffer, bool vsync, io::IFileSystem* io);
    }

} // end namespace irr



struct SEnvMapper
{
    HWND hWnd;
    irr::CIrrDeviceWin32* irrDev;
};

irr::core::list<SEnvMapper> EnvMap;

SEnvMapper* getEnvMapperFromHWnd(HWND hWnd)
{
    irr::core::list<SEnvMapper>::Iterator it = EnvMap.begin();
    for (; it!= EnvMap.end(); ++it)
        if ((*it).hWnd == hWnd)
            return &(*it);

    return 0;
}

irr::CIrrDeviceWin32* getDeviceFromHWnd(HWND hWnd)
{
    irr::core::list<SEnvMapper>::Iterator it = EnvMap.begin();
    for (; it!= EnvMap.end(); ++it)
        if ((*it).hWnd == hWnd)
            return (*it).irrDev;

    return 0;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL 0x020A
#endif
#ifndef WHEEL_DELTA                     
#define WHEEL_DELTA 120
#endif

    irr::CIrrDeviceWin32* dev = 0;
    irr::SEvent event;
    SEnvMapper* envm = 0;

    switch (message) 
    {
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            EndPaint(hWnd, &ps);
        }
        return 0;

    case WM_ERASEBKGND:
        return 0;

    case WM_SETCURSOR: 
        envm = getEnvMapperFromHWnd(hWnd);
        if (envm &&	!envm->irrDev->getWin32CursorControl()->isVisible())
        {
            SetCursor(NULL); 
            return 0; 
        }
        break;

    case WM_MOUSEWHEEL: 
        event.EventType = irr::EET_MOUSE_INPUT_EVENT; 
        event.MouseInput.Wheel = (float)((short)HIWORD(wParam)) / (float)WHEEL_DELTA;
        event.MouseInput.Event = irr::EMIE_MOUSE_WHEEL;

        POINT p; // fixed by jox
        p.x = 0; p.y = 0;
        ClientToScreen(hWnd, &p);
        event.MouseInput.X = LOWORD(lParam) - p.x;
        event.MouseInput.Y = HIWORD(lParam) - p.y; 

        dev = getDeviceFromHWnd(hWnd); 
        if (dev) 
            dev->postEventFromUser(event); 
        break;

    case WM_LBUTTONDOWN:
        event.EventType = irr::EET_MOUSE_INPUT_EVENT;
        event.MouseInput.Event = irr::EMIE_LMOUSE_PRESSED_DOWN;
        event.MouseInput.X = LOWORD(lParam);
        event.MouseInput.Y = HIWORD(lParam);
        dev = getDeviceFromHWnd(hWnd);
        if (dev)
            dev->postEventFromUser(event);
        return 0;

    case WM_LBUTTONUP:
        event.EventType = irr::EET_MOUSE_INPUT_EVENT;
        event.MouseInput.Event = irr::EMIE_LMOUSE_LEFT_UP;
        event.MouseInput.X = LOWORD(lParam);
        event.MouseInput.Y = HIWORD(lParam);
        dev = getDeviceFromHWnd(hWnd);
        if (dev)
            dev->postEventFromUser(event);
        return 0;

    case WM_RBUTTONDOWN:
        event.EventType = irr::EET_MOUSE_INPUT_EVENT;
        event.MouseInput.Event = irr::EMIE_RMOUSE_PRESSED_DOWN;
        event.MouseInput.X = LOWORD(lParam);
        event.MouseInput.Y = HIWORD(lParam);
        dev = getDeviceFromHWnd(hWnd);
        if (dev)
            dev->postEventFromUser(event);
        return 0;

    case WM_RBUTTONUP:
        event.EventType = irr::EET_MOUSE_INPUT_EVENT;
        event.MouseInput.Event = irr::EMIE_RMOUSE_LEFT_UP;
        event.MouseInput.X = LOWORD(lParam);
        event.MouseInput.Y = HIWORD(lParam);
        dev = getDeviceFromHWnd(hWnd);
        if (dev)
            dev->postEventFromUser(event);
        return 0;

    case WM_MBUTTONDOWN:
        event.EventType = irr::EET_MOUSE_INPUT_EVENT;
        event.MouseInput.Event = irr::EMIE_MMOUSE_PRESSED_DOWN;
        event.MouseInput.X = LOWORD(lParam);
        event.MouseInput.Y = HIWORD(lParam);
        dev = getDeviceFromHWnd(hWnd);
        if (dev)
            dev->postEventFromUser(event);
        return 0;

    case WM_MBUTTONUP:
        event.EventType = irr::EET_MOUSE_INPUT_EVENT;
        event.MouseInput.Event = irr::EMIE_MMOUSE_LEFT_UP;
        event.MouseInput.X = LOWORD(lParam);
        event.MouseInput.Y = HIWORD(lParam);
        dev = getDeviceFromHWnd(hWnd);
        if (dev)
            dev->postEventFromUser(event);
        return 0;

    case WM_MOUSEMOVE:
        event.EventType = irr::EET_MOUSE_INPUT_EVENT;
        event.MouseInput.Event = irr::EMIE_MOUSE_MOVED;
        event.MouseInput.X = LOWORD(lParam);
        event.MouseInput.Y = HIWORD(lParam);
        dev = getDeviceFromHWnd(hWnd);
        if (dev)
            dev->postEventFromUser(event);
        return 0;

    case WM_KEYDOWN:
        {
            event.EventType = irr::EET_KEY_INPUT_EVENT;
            event.KeyInput.Key = (irr::EKEY_CODE)wParam;
            event.KeyInput.PressedDown = true;
            dev = getDeviceFromHWnd(hWnd);

            BYTE allKeys[256];
            WORD KeyAsc=0;
            GetKeyboardState(allKeys);
            ToAscii(wParam,lParam,allKeys,&KeyAsc,0);

            event.KeyInput.Shift = ((allKeys[VK_SHIFT] & 0x80)!=0);
            event.KeyInput.Control = ((allKeys[VK_CONTROL] & 0x80)!=0);
            event.KeyInput.Char = KeyAsc; //KeyAsc >= 0 ? KeyAsc : 0;

            if (dev)
                dev->postEventFromUser(event);

            return 0;
        }
    case WM_KEYUP:
        {
            event.EventType = irr::EET_KEY_INPUT_EVENT;
            event.KeyInput.Key = (irr::EKEY_CODE)wParam;
            event.KeyInput.PressedDown = false;
            dev = getDeviceFromHWnd(hWnd);

            BYTE allKeys[256];
            WORD KeyAsc=0;
            GetKeyboardState(allKeys);
            ToAscii(wParam,lParam,allKeys,&KeyAsc,0);

            event.KeyInput.Shift = ((allKeys[VK_SHIFT] & 0x80)!=0);
            event.KeyInput.Control = ((allKeys[VK_CONTROL] & 0x80)!=0);
            event.KeyInput.Char = KeyAsc; //KeyAsc >= 0 ? KeyAsc : 0;

            if (dev)
                dev->postEventFromUser(event);

            return 0;
        }

    case WM_SIZE:
        {
            // resize
            dev = getDeviceFromHWnd(hWnd);
            if (dev)
                dev->OnResized();
        }
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_SYSCOMMAND:
        // prevent screensaver or monitor powersave mode from starting
        if (wParam == SC_SCREENSAVE ||
            wParam == SC_MONITORPOWER)
            return 0;
        break;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

namespace irr
{

    //! constructor
    CIrrDeviceWin32::CIrrDeviceWin32(video::E_DRIVER_TYPE driverType, 
        core::dimension2d<s32> windowSize,
        u32 bits, bool fullscreen, 
        bool stencilbuffer, bool vsync, 
        bool antiAlias,
        IEventReceiver* receiver,
        HWND externalWindow,
        const char* version)
        : CIrrDeviceStub(version, receiver), HWnd(0), ChangedToFullScreen(false),
        Win32CursorControl(0), IsNonNTWindows(false), Resized(false),
        FullScreen(fullscreen), ExternalWindow(false)
    {
        core::stringc winversion;
        getWindowsVersion(winversion);
        Operator = new COSOperator(winversion.c_str());

        // create window
        HINSTANCE hInstance = GetModuleHandle(0);

#ifdef _DEBUG
        setDebugName("CIrrDeviceWin32");	
#endif

        if (driverType != video::EDT_NULL && externalWindow==0)
        {
            // create the window, only if we do not use the null device

            const c8* ClassName = "CIrrDeviceWin32";

            // Register Class

            WNDCLASSEX wcex;
            wcex.cbSize = sizeof(WNDCLASSEX); 
            wcex.style			= CS_HREDRAW | CS_VREDRAW;
            wcex.lpfnWndProc	= (WNDPROC)WndProc;
            wcex.cbClsExtra		= 0;
            wcex.cbWndExtra		= 0;
            wcex.hInstance		= hInstance;
            wcex.hIcon			= NULL;
            wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
            wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
            wcex.lpszMenuName	= 0;
            wcex.lpszClassName	= ClassName;
            wcex.hIconSm		= 0;

            // if there is an icon, load it
            wcex.hIcon = (HICON)LoadImage(hInstance, "irr.ico", IMAGE_ICON, 0,0, LR_LOADFROMFILE); 

            RegisterClassEx(&wcex);

            // calculate client size

            RECT clientSize;
            clientSize.top = 0;
            clientSize.left = 0;
            clientSize.right = windowSize.Width;
            clientSize.bottom = windowSize.Height;

            DWORD style = WS_POPUP;

            if (!fullscreen)
                style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

            AdjustWindowRect(&clientSize, style, FALSE);

            s32 realWidth = clientSize.right - clientSize.left;
            s32 realHeight = clientSize.bottom - clientSize.top;

            s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
            s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;

            if (fullscreen)
            {
                windowLeft = 0;
                windowTop = 0;
            }

            // create window

            HWnd = CreateWindow( ClassName, "", style, windowLeft, windowTop, 
                realWidth, realHeight,	NULL, NULL, hInstance, NULL);

            ShowWindow(HWnd , SW_SHOW);
            UpdateWindow(HWnd);

            // fix ugly ATI driver bugs. Thanks to ariaci
            MoveWindow(HWnd, windowLeft, windowTop, realWidth, realHeight, TRUE);
        }

        // attach external window
        if (externalWindow)
        {
            HWnd = externalWindow;
            RECT r;
            GetWindowRect(HWnd, &r);
            windowSize.Width = r.right - r.left;
            windowSize.Height = r.bottom - r.top;
            fullscreen = false;
            ExternalWindow = true;
        }

        // create cursor control

        Win32CursorControl = new CCursorControl(windowSize, HWnd, fullscreen);
        CursorControl = Win32CursorControl;

        // create driver

        createDriver(driverType, windowSize, bits, fullscreen, stencilbuffer, vsync, antiAlias);

        createGUIAndScene();

        // register environment

        SEnvMapper em;
        em.irrDev = this;
        em.hWnd = HWnd;
        EnvMap.push_back(em);

        // set this as active window
        SetActiveWindow(HWnd);
        SetForegroundWindow(HWnd);
    }



    //! destructor
    CIrrDeviceWin32::~CIrrDeviceWin32()
    {
        // unregister environment

        irr::core::list<SEnvMapper>::Iterator it = EnvMap.begin();
        for (; it!= EnvMap.end(); ++it)
            if ((*it).hWnd == HWnd)
            {
                EnvMap.erase(it);
                break;
            }

            if (ChangedToFullScreen)
                ChangeDisplaySettings(NULL,0);
    }



    //! create the driver
    void CIrrDeviceWin32::createDriver(video::E_DRIVER_TYPE driverType,
        const core::dimension2d<s32>& windowSize,
        u32 bits, 
        bool fullscreen,
        bool stencilbuffer,
        bool vsync,
        bool antiAlias)
    {
        switch(driverType)
        {
        case video::EDT_DIRECT3D8:
#ifdef _IRR_COMPILE_WITH_DIRECT3D_8_
            VideoDriver = video::createDirectX8Driver(windowSize, HWnd, bits, fullscreen, 
                stencilbuffer, FileSystem, false, vsync, antiAlias);
#endif

            if (!VideoDriver)
            {
#ifdef _IRR_COMPILE_WITH_DIRECT3D_8_
                os::Printer::log("Could not create DIRECT3D8 Driver.", ELL_ERROR);
#else
                os::Printer::log("DIRECT3D8 Driver was not compiled into this dll. Try another one.", ELL_ERROR);
#endif
            }

            break;	

        case video::EDT_DIRECT3D9:
#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
            VideoDriver = video::createDirectX9Driver(windowSize, HWnd, bits, fullscreen, 
                stencilbuffer, FileSystem, false, vsync, antiAlias);
#endif

            if (!VideoDriver)
            {
#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
                os::Printer::log("Could not create DIRECT3D9 Driver.", ELL_ERROR);
#else
                os::Printer::log("DIRECT3D9 Driver was not compiled into this dll. Try another one.", ELL_ERROR);
#endif
            }

            break;	

        case video::EDT_OPENGL:

#ifdef _IRR_COMPILE_WITH_OPENGL_
            if (fullscreen)	switchToFullScreen(windowSize.Width, windowSize.Height, bits);
            VideoDriver = video::createOpenGLDriver(windowSize, HWnd, fullscreen, stencilbuffer,
                vsync, FileSystem);
#endif

            if (!VideoDriver)
            {
#ifdef _IRR_COMPILE_WITH_OPENGL_
                os::Printer::log("Could not create OpenGL driver.", ELL_ERROR);
#else
                os::Printer::log("OpenGL driver was not compiled into this dll.", ELL_ERROR);
#endif
            }
            break;	

        case video::EDT_SOFTWARE:
            if (fullscreen)	switchToFullScreen(windowSize.Width, windowSize.Height, bits);
            VideoDriver = video::createSoftwareDriver(windowSize, fullscreen, FileSystem, this);
            break;

        case video::EDT_SOFTWARE2:
            if (fullscreen)	switchToFullScreen(windowSize.Width, windowSize.Height, bits);
            VideoDriver = video::createSoftwareDriver2(windowSize, fullscreen, FileSystem, this);
            break;

        default:
            // create null driver if a bad programmer made a mistake
            VideoDriver = video::createNullDriver(FileSystem, windowSize);
            break;
        }
    }



    //! runs the device. Returns false if device wants to be deleted
    bool CIrrDeviceWin32::run()
    {
        os::Timer::tick();

        MSG msg;

        bool quit = false;

        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);

            if (ExternalWindow && msg.hwnd == HWnd)
                WndProc(HWnd, msg.message, msg.wParam, msg.lParam);
            else
                DispatchMessage(&msg);

            if (msg.message == WM_QUIT)
                quit = true;
        }

        if (!quit)
            resizeIfNecessary();

        _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
        return !quit;
    }


    void CIrrDeviceWin32::resizeIfNecessary()
    {
        if (!Resized)
            return;

        RECT r;
        GetClientRect(HWnd, &r);

        char tmp[255];

        if (r.right <= 1 || r.bottom <= 1)
        {
            sprintf(tmp, "Ignoring resize operation to (%d %d)", r.right, r.bottom);
            os::Printer::log(tmp);
        }
        else
        {
            sprintf(tmp, "Resizing window (%d %d)", r.right, r.bottom);
            os::Printer::log(tmp);

            if ( r.right % 2 )
                r.right += 1;

            if ( r.bottom % 2 )
                r.bottom += 1;

            getVideoDriver()->OnResize(irr::core::dimension2d<s32>(r.right, r.bottom));
        }

        Resized = false;
    }



    //! sets the caption of the window
    void CIrrDeviceWin32::setWindowCaption(const wchar_t* text)
    {
        if (IsNonNTWindows)
        {
            core::stringc s = text;
            SetWindowTextA(HWnd, s.c_str());
        }
        else
            SetWindowTextW(HWnd, text);
    }



    //! presents a surface in the client area
    void CIrrDeviceWin32::present(video::IImage* image, s32 windowId, core::rect<s32>* src )
    {
        HWND hwnd = HWnd;
        if ( windowId )
            hwnd = (HWND)windowId;

        HDC dc = GetDC(hwnd);

        if ( dc )
        {
            RECT rect;
            GetClientRect(hwnd, &rect);
            const void* memory = (const void *)image->lock();

            BITMAPV4HEADER bi;
            ZeroMemory (&bi, sizeof(bi));
            bi.bV4Size          = sizeof(BITMAPINFOHEADER);
            bi.bV4BitCount      = image->getBitsPerPixel();
            bi.bV4Planes        = 1;
            bi.bV4Width         = image->getDimension().Width;
            bi.bV4Height        = -image->getDimension().Height;
            bi.bV4V4Compression = BI_BITFIELDS;
            bi.bV4AlphaMask     = image->getAlphaMask ();
            bi.bV4RedMask       = image->getRedMask ();
            bi.bV4GreenMask     = image->getGreenMask();
            bi.bV4BlueMask      = image->getBlueMask();

            if ( src )
            {
                StretchDIBits(dc, 0,0, rect.right, rect.bottom,
                    src->UpperLeftCorner.X, src->UpperLeftCorner.Y,
                    src->getWidth(), src->getHeight(),
                    memory, (const BITMAPINFO*)(&bi), DIB_RGB_COLORS, SRCCOPY);
            }
            else
            {
                StretchDIBits(dc, 0,0, rect.right, rect.bottom,
                    0, 0, image->getDimension().Width, image->getDimension().Height,
                    memory, (const BITMAPINFO*)(&bi), DIB_RGB_COLORS, SRCCOPY);
            }

            image->unlock();

            ReleaseDC(hwnd, dc);
        }
    }



    //! notifies the device that it should close itself
    void CIrrDeviceWin32::closeDevice()
    {
        DestroyWindow(HWnd); 	//PostQuitMessage(0);
    }



    //! returns if window is active. if not, nothing need to be drawn
    bool CIrrDeviceWin32::isWindowActive()
    {
        bool ret = (GetActiveWindow() == HWnd);
        _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;	
        return ret;
    }



    //! switchs to fullscreen
    bool CIrrDeviceWin32::switchToFullScreen(s32 width, s32 height, s32 bits)
    {
        DEVMODE dm;
        memset(&dm, 0, sizeof(dm));
        dm.dmSize = sizeof(dm);
        dm.dmPelsWidth = width;
        dm.dmPelsHeight	= height;
        dm.dmBitsPerPel	= bits;
        dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

        LONG ret = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);

        switch(ret)
        {
        case DISP_CHANGE_SUCCESSFUL:
            ChangedToFullScreen = true;
            return true;
        case DISP_CHANGE_RESTART:
            os::Printer::log("Switch to fullscreen: The computer must be restarted in order for the graphics mode to work.", ELL_ERROR);
            return false;
        case DISP_CHANGE_BADFLAGS:
            os::Printer::log("Switch to fullscreen: An invalid set of flags was passed in.", ELL_ERROR);
            return false;
        case DISP_CHANGE_BADPARAM:
            os::Printer::log("Switch to fullscreen: An invalid parameter was passed in. This can include an invalid flag or combination of flags.", ELL_ERROR);
            return false;
        case DISP_CHANGE_FAILED:
            os::Printer::log("Switch to fullscreen: The display driver failed the specified graphics mode.", ELL_ERROR);
            return false;
        case DISP_CHANGE_BADMODE:
            os::Printer::log("Switch to fullscreen: The graphics mode is not supported.", ELL_ERROR);
            return false;
        }

        os::Printer::log("An unknown error occured while changing to fullscreen.", ELL_ERROR);
        return false;
    }


    //! returns the win32 cursor control
    CIrrDeviceWin32::CCursorControl* CIrrDeviceWin32::getWin32CursorControl()
    {
        return Win32CursorControl;
    }


    //! \return Returns a pointer to a list with all video modes supported
    //! by the gfx adapter.
    video::IVideoModeList* CIrrDeviceWin32::getVideoModeList()
    {
        if (!VideoModeList.getVideoModeCount())
        {
            // enumerate video modes.
            DWORD i=0;
            DEVMODE mode;

            while (EnumDisplaySettings(NULL, i, &mode))
            {
                VideoModeList.addMode(core::dimension2d<s32>(mode.dmPelsWidth, mode.dmPelsHeight),
                    mode.dmBitsPerPel);

                ++i;
            }

            if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &mode))
                VideoModeList.setDesktop(mode.dmBitsPerPel, core::dimension2d<s32>(mode.dmPelsWidth, mode.dmPelsHeight));
        }

        return &VideoModeList;
    }


    void CIrrDeviceWin32::getWindowsVersion(core::stringc& out)
    {
        OSVERSIONINFOEX osvi;
        BOOL bOsVersionInfoEx;

        ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
        osvi.dwOSVersionInfoSize	= sizeof(OSVERSIONINFOEX);

        if(!(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO*) &osvi)))
        {
            osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
            if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) 
                return;
        }

        switch (osvi.dwPlatformId)
        {
        case VER_PLATFORM_WIN32_NT:
            if (osvi.dwMajorVersion <= 4)
                out.append("Microsoft Windows NT ");
            if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
                out.append("Microsoft Windows 2000 ");
            if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
                out.append("Microsoft Windows XP ");

            if( bOsVersionInfoEx )
            {
#ifdef VER_SUITE_ENTERPRISE
                if (osvi.wProductType == VER_NT_WORKSTATION)
                {
                    if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
                        out.append("Personal ");
                    else
                        out.append("Professional ");
                }
                else if (osvi.wProductType == VER_NT_SERVER)
                {
                    if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
                        out.append("DataCenter Server ");
                    else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
                        out.append("Advanced Server ");
                    else
                        out.append("Server ");
                }
#endif
            }
            else
            {
                HKEY hKey;
                char szProductType[80];
                DWORD dwBufLen;

                RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                    "SYSTEM\\CurrentControlSet\\Control\\ProductOptions", 
                    0, KEY_QUERY_VALUE, &hKey );
                RegQueryValueEx( hKey, "ProductType", NULL, NULL,
                    (LPBYTE) szProductType, &dwBufLen);
                RegCloseKey( hKey );

                if (lstrcmpi( "WINNT", szProductType) == 0 )
                    out.append("Professional ");
                if ( lstrcmpi( "LANMANNT", szProductType) == 0 )
                    out.append("Server " );
                if ( lstrcmpi( "SERVERNT", szProductType) == 0 )
                    out.append("Advanced Server ");
            }

            // Display version, service pack (if any), and build number.

            char tmp[255];

            if (osvi.dwMajorVersion <= 4 )
            {
                sprintf (tmp, "version %d.%d %s (Build %d)",
                    osvi.dwMajorVersion,
                    osvi.dwMinorVersion,
                    osvi.szCSDVersion,
                    osvi.dwBuildNumber & 0xFFFF);
            }
            else
            {
                sprintf (tmp, "%s (Build %d)", osvi.szCSDVersion, 
                    osvi.dwBuildNumber & 0xFFFF);
            }

            out.append(tmp);
            break;

        case VER_PLATFORM_WIN32_WINDOWS:

            IsNonNTWindows = true;

            if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
            {
                out.append("Microsoft Windows 95 ");
                if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' )
                    out.append("OSR2 " );
            } 

            if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
            {
                out.append("Microsoft Windows 98 ");
                if ( osvi.szCSDVersion[1] == 'A' )
                    out.append( "SE " );
            } 

            if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
                out.append("Microsoft Windows Me ");

            break;

        case VER_PLATFORM_WIN32s:

            IsNonNTWindows = true;
            out.append("Microsoft Win32s ");
            break;
        }
    }

    //! Notifies the device, that it has been resized
    void CIrrDeviceWin32::OnResized()
    {
        Resized = true;
    }

    //! Sets if the window should be resizeable in windowed mode.
    void CIrrDeviceWin32::setResizeAble(bool resize)
    {
        if (ExternalWindow || !getVideoDriver() || FullScreen)
            return;

        LONG style = WS_POPUP;

        if (!resize)
            style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
        else
            style = WS_THICKFRAME | WS_SYSMENU | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MAXIMIZEBOX;

        if (!SetWindowLong(HWnd, GWL_STYLE, style))
            os::Printer::log("Could not change window style.");

        RECT clientSize;
        clientSize.top = 0;
        clientSize.left = 0;
        clientSize.right = getVideoDriver()->getScreenSize().Width;
        clientSize.bottom = getVideoDriver()->getScreenSize().Height;

        AdjustWindowRect(&clientSize, style, FALSE);

        s32 realWidth = clientSize.right - clientSize.left;
        s32 realHeight = clientSize.bottom - clientSize.top;

        s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
        s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;

        SetWindowPos(HWnd, HWND_TOP, windowLeft, windowTop, realWidth, realHeight, 
            SWP_FRAMECHANGED | SWP_NOMOVE | SWP_SHOWWINDOW);
    }

#ifdef IRRLICHT_EXPORTS
#define IRRLICHT_API __declspec(dllexport)
#else
#define IRRLICHT_API __declspec(dllimport)
#endif // IRRLICHT_EXPORT

#if defined(_STDCALL_SUPPORTED)
#define IRRCALLCONV __stdcall  // Declare the calling convention.
#else
#define IRRCALLCONV
#endif // STDCALL_SUPPORTED


    IRRLICHT_API IrrlichtDevice* IRRCALLCONV createDeviceEx(
        const SIrrlichtCreationParameters& parameters)
    {
        CIrrDeviceWin32* dev = new CIrrDeviceWin32(
            parameters.DriverType,
            parameters.WindowSize,
            parameters.Bits,
            parameters.Fullscreen,
            parameters.Stencilbuffer,
            parameters.Vsync,
            parameters.AntiAlias,
            parameters.EventReceiver,
            reinterpret_cast<HWND>(parameters.WindowId),
            parameters.SDK_version_do_not_use);

        if (dev && !dev->getVideoDriver() && parameters.DriverType != video::EDT_NULL)
        {
            dev->closeDevice(); // destroy window
            dev->run(); // consume quit message
            dev->drop();
            dev = 0;
        }

        return dev;
    }


} // end namespace 

#endif // _IRR_WINDOWS_

